﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using blackhouse.Data;
using System.Data;

namespace blackhouse.BlackApart.Data.Caching {
    public class NemoCache {

        #region Fields

        private TimeSpan cacheInterval;

        private string currentKey;
        private string queryIdentifier;
        private string[] dependOnTables;

        private MsSqlAdoDataHelper dataHelper;

        private HttpContext context;
        private bool contextChecked = false;

        #endregion

        #region Constructors

        public NemoCache(TimeSpan cacheInterval, string sqlConnectionString) {
            this.cacheInterval = cacheInterval;
            this.dataHelper = new MsSqlAdoDataHelper(sqlConnectionString);
        }

        #endregion

        #region Methods

        public void SetKey(string key) {
            this.currentKey = key;
        }

        public void SetParameters(params object[] parameters) {
            queryIdentifier = string.Join("-", parameters.Select(p => p.ToString()).ToArray());
        }

        public void SetDependencyTables(params string[] tables) {
            this.dependOnTables = tables;
        }

        public void Reset() {
            this.currentKey = "";
            this.dependOnTables = null;
            this.queryIdentifier = "";
        }

        public void TouchTimes() {
            if (this.IsWebRequest())
                this.context.Cache.Remove("NemoTimes");
        }

        public T GetData<T>() {
            if (this.IsWebRequest()) {
                NemoCacheObject cache = this.context.Cache.Get(this.GetCurrentCacheKey()) as NemoCacheObject;
                if (cache == null) return default(T);
                DateTime dbTime = this.GetLastUpdateTime();
                if (cache.SetTime < dbTime) return default(T);
                return (T) cache.CachedData;
            } else return default(T);
        }

        private DateTime GetLastUpdateTime() {
            var q = from t in this.GetTimes()
                    join tb in this.dependOnTables on t.TableName equals tb
                    select t.LastUpdateTime;
            if (q.Count() == 0)
                return DateTime.MinValue;
            else
                return q.Max();
        }

        private string GetCurrentCacheKey() {
            return this.currentKey + "_" + queryIdentifier;
        }

        private IEnumerable<NemoTableTime> GetTimes() {
            if (this.IsWebRequest()) {
                IEnumerable<NemoTableTime> times = this.context.Cache.Get("NemoTimes") as IEnumerable<NemoTableTime>;
                if (times == null) {
                    times = this.GetTimesFromDb();
                    this.context.Cache.Insert("NemoTimes", times, null, DateTime.Now.Add(this.cacheInterval),  System.Web.Caching.Cache.NoSlidingExpiration);
                }
                return times;
            } else return new NemoTableTime[0];
        }

        private IEnumerable<NemoTableTime> GetTimesFromDb() {
            this.dataHelper.Reset();
            this.dataHelper.ProcedureName = "bhp_TablesLastModificationTimeGet";
            this.dataHelper.AddParameter("AppTime", DateTime.Now, DbType.DateTime);
            return this.dataHelper.Execute<NemoTableTime>(ReadNemoTableTimeFields);
        }

        private NemoTableTime ReadNemoTableTimeFields(IDataReader reader) {
            string tableName;
            DateTime lastUpdate = DateTime.MinValue;
            tableName = Convert.ToString(reader["Table"]);
            if (!reader.IsDBNull(reader.GetOrdinal("LastUpdate")))
                lastUpdate = Convert.ToDateTime(reader["LastUpdate"]);
            return new NemoTableTime(tableName, lastUpdate);
        }

        private bool IsWebRequest() {
            if (this.contextChecked) return this.context != null;
            else {
                this.context = HttpContext.Current;
                this.contextChecked = true;
                return this.IsWebRequest();
            }
        }

        public void SetCaching(object data) {
            if (this.IsWebRequest()) {
                this.context.Cache.Insert(this.GetCurrentCacheKey(), new NemoCacheObject(data), null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(20));
            }
        }

        #endregion

    }
}
